package me.luger.core.dao.nutz.service;

import me.luger.core.commom.util.LogUtil;
import me.luger.core.dao.nutz.callback.ListEntityCallback;
import me.luger.core.dao.nutz.entity.page.*;
import org.nutz.dao.*;
import org.nutz.dao.entity.Entity;
import org.nutz.dao.entity.Record;
import org.nutz.dao.impl.sql.callback.FetchLongCallback;
import org.nutz.dao.impl.sql.callback.FetchRecordCallback;
import org.nutz.dao.pager.Pager;
import org.nutz.dao.sql.Sql;
import org.nutz.dao.util.Daos;
import org.nutz.lang.Lang;
import org.nutz.lang.Mirror;
import org.nutz.lang.Strings;
import org.nutz.lang.util.NutMap;
import org.nutz.service.Service;

import java.util.*;

/**
 * BaseDao
 *
 * @author luger
 * @date 2016/10/17 0017
 */
public class BaseService<T> extends Service {
    protected final static int DEFAULT_PAGE_NUMBER = 10;

    private Mirror<T> mirror;

    /**
     * 本抽象类能提供一些帮助方法,减少重复写实体类型的麻烦
     */
    @SuppressWarnings("unchecked")
    public BaseService() {
        try {
            Class<T> entryClass = (Class<T>) Mirror.getTypeParam(getClass(), 0);
            mirror = Mirror.me(entryClass);
            LogUtil.debugLog("Get TypeParams for self : %s", entryClass.getName());
        } catch (Throwable e) {
            LogUtil.errorLog(e, "!!!Fail to get TypeParams for self!");
        }
    }

    /**
     * 新建并传入Dao实例
     *
     * @param dao Dao实例
     */
    public BaseService(Dao dao) {
        this();
        this.setDao(dao);
    }

    /**
     * 新建并传入Dao实例,同时指定实体类型
     *
     * @param dao        Dao实例
     * @param entityType 实体类型
     */
    public BaseService(Dao dao, Class<T> entityType) {
        setEntityType(entityType);
        setDao(dao);
    }

    /**
     * 获取实体类型的反射封装类实例
     *
     * @return 反射封装类实例
     */
    public Mirror<T> mirror() {
        return mirror;
    }

    /**
     * 设置新的实体类型, 极少调用
     *
     * @param classOfT
     */
    @SuppressWarnings("unchecked")
    public <C extends T> void setEntityType(Class<C> classOfT) {
        mirror = (Mirror<T>) Mirror.me(classOfT);
    }

    /**
     * 获取实体的Entity
     *
     * @return 实体的Entity
     */
    public Entity<T> getEntity() {
        return dao().getEntity(mirror.getType());
    }

    /**
     * 获取实体类型
     *
     * @return 实体类型
     */
    public Class<T> getEntityClass() {
        return mirror.getType();
    }

    /******************************************************************************/
    /**
     * 默认页码
     *
     * @param pageNumber
     * @return
     */
    protected int getPageNumber(Integer pageNumber) {
        return Lang.isEmpty(pageNumber) ? 1 : pageNumber;
    }

    /**
     * 默认页大小
     *
     * @param pageSize
     * @return
     */
    protected int getPageSize(int pageSize) {
        return pageSize == 0 ? DEFAULT_PAGE_NUMBER : pageSize;
    }

    /*********************************增********************************************/
    public T insert(T t) {
        return this.dao().insert(t);
    }

    public T fastInsert(T t) {
        return this.dao().fastInsert(t);
    }

    /*********************************删********************************************/
    /**
     * 批量删除
     *
     * @param cnd 条件
     * @return 删除的条数
     */
    public int delete(Condition cnd) {
        return dao().clear(getEntityClass(), cnd);
    }

    /**
     * 批量删除
     *
     * @param ids
     */
    public void delete(Integer[] ids) {
        this.dao().clear(getEntityClass(), Cnd.where("id", "in", ids));
    }

    /**
     * 批量删除
     *
     * @param ids
     */
    public void delete(Long[] ids) {
        this.dao().clear(getEntityClass(), Cnd.where("id", "in", ids));
    }

    /**
     * 批量删除
     *
     * @param ids
     */
    public void delete(String[] ids) {
        this.dao().clear(getEntityClass(), Cnd.where("id", "in", ids));
    }

    /**
     * 全表删除
     *
     * @return 删除的条数
     */
    public int deleteAll() {
        return dao().clear(getEntityClass(), null);
    }

    /*********************************改********************************************/
    public int update(Object obj) {
        return this.dao().update(obj);
    }

    /**
     * 忽略值为null的字段
     *
     * @param t
     * @return
     */
    public int updateIgnoreNull(Object t) {
        return this.dao().updateIgnoreNull(t);
    }

    /*********************************查********************************************/

    public List<T> list(){
        return list(Cnd.NEW());
    }

    public List<T> list(Sql sql){
        sql.setEntity(getEntity());
        sql.setCallback(new ListEntityCallback(null));
        dao().execute(sql);
        return sql.getList(getEntityClass());
    }

    public List list(Sql sql,Class clazz){
        sql.setEntity(getEntity());
        sql.setCallback(new ListEntityCallback(null));
        dao().execute(sql);
        return sql.getList(clazz);
    }

    public List<T> list(Condition cnd){
        List<T> list = this.dao().query(getEntityClass(), cnd);
        return list;
    }



    /**
     * 分页查询(cnd)
     *
     * @param pageNumber
     * @param pageSize
     * @param cnd
     * @return
     */
    public Pagination listPage(Integer pageNumber, int pageSize, Condition cnd) {
        pageNumber = getPageNumber(pageNumber);
        pageSize = getPageSize(pageSize);
        Pager pager = this.dao().createPager(pageNumber, pageSize);
        List<T> list = this.dao().query(getEntityClass(), cnd, pager);
        pager.setRecordCount(this.dao().count(getEntityClass(), cnd));
        return new Pagination(pageNumber, pageSize, pager.getRecordCount(), list);
    }

    /**
     * 分页查询(sql)
     *
     * @param pageNumber
     * @param pageSize
     * @param sql
     * @return
     */
    public Pagination listPage(Integer pageNumber, int pageSize, Sql sql) {
        pageNumber = getPageNumber(pageNumber);
        pageSize = getPageSize(pageSize);
        Pager pager = this.dao().createPager(pageNumber, pageSize);
        pager.setRecordCount((int) Daos.queryCount(this.dao(), sql.toString()));// 记录数需手动设置
        sql.setPager(pager);
        sql.setCallback(new ListEntityCallback());
        sql.setEntity(getEntity());
        dao().execute(sql);
        return new Pagination(pageNumber, pageSize, pager.getRecordCount(), sql.getList(getEntityClass()));
    }

    /**
     * DataTable Page
     *
     * @param length   页大小
     * @param start    start
     * @param draw     draw
     * @param orders   排序
     * @param columns  字段
     * @param cnd      查询条件
     * @return
     */
    public DataTableResult<T> data(int length, int start, int draw, List<DataTableOrder> orders, List<DataTableColumn> columns, Cnd cnd, String linkname) {
        DataTableResult<T> dataTablesResult = new DataTableResult<T>();
        if (orders != null && orders.size() > 0) {
            for (DataTableOrder order : orders) {
                DataTableColumn col = columns.get(order.getColumn());
                if(null == cnd){
                    cnd = Cnd.NEW();
                }
                cnd.orderBy(Sqls.escapeSqlFieldValue(col.getData()).toString(), order.getDir());
            }
        }
        Pager pager = new OffsetPager(start, length);
        dataTablesResult.setRecordsFiltered(this.dao().count(getEntityClass(), cnd));
        List<T> list = this.dao().query(getEntityClass(), cnd, pager);
        if (!Strings.isBlank(linkname)) {
            this.dao().fetchLinks(list, linkname);
        }
        dataTablesResult.setData(list);
        dataTablesResult.setDraw(draw);
        dataTablesResult.setRecordsTotal(length);
        return dataTablesResult;
    }

    /**
     * DataTable Page SQL
     *
     * @param length   页大小
     * @param start    start
     * @param draw     draw
     * @param countSql 查询条件
     * @param orderSql 排序语句
     * @return
     */
    public DataTableResult<T> data(int length, int start, int draw, Sql countSql, Sql orderSql) {
        DataTableResult<T> dataTablesResult = new DataTableResult<T>();
        Pager pager = new OffsetPager(start, length);
        pager.setRecordCount((int) Daos.queryCount(this.dao(), countSql.toString()));// 记录数需手动设置
        orderSql.setPager(pager);
        orderSql.setCallback(new ListEntityCallback());
        this.dao().execute(orderSql);
        dataTablesResult.setRecordsFiltered(pager.getRecordCount());
        dataTablesResult.setData(orderSql.getList(getEntityClass()));
        dataTablesResult.setDraw(draw);
        dataTablesResult.setRecordsTotal(length);
        return dataTablesResult;
    }

    public DataTableResult<T> data(DataTableRequest dataTableRequest){
        return data(dataTableRequest,Cnd.NEW());
    }


    public DataTableResult<T> data(DataTableRequest dataTableRequest, Cnd cnd){
        return data(dataTableRequest.getLength(),dataTableRequest.getStart(),dataTableRequest.getDraw(),dataTableRequest.getOrder(),dataTableRequest.getColumns(),cnd,null);
    }

    public DataTableResult<T> data(DataTableRequest dataTableRequest, Sql sql){
        return data(dataTableRequest.getLength(),dataTableRequest.getStart(),dataTableRequest.getDraw(),dataTableRequest.getOrder(),dataTableRequest.getColumns(),sql,null);
    }

    public DataTableResult<T> data(int length, int start, int draw, List<DataTableOrder> orders, List<DataTableColumn> columns, Sql sql, String linkname) {

        DataTableResult<T> dataTablesResult = new DataTableResult<T>();
        Cnd cnd = Cnd.NEW();
        if (orders != null && orders.size() > 0) {
            for (DataTableOrder order : orders) {
                DataTableColumn col = columns.get(order.getColumn());
                cnd.orderBy(Sqls.escapeSqlFieldValue(col.getData()).toString(), order.getDir());
            }
        }
        sql.setCondition(cnd);
        sql.setCallback(new ListEntityCallback());
        Pager pager = new OffsetPager(start, length);
        pager.setPageNumber(start/length+1);
        dataTablesResult.setRecordsFiltered(this.dao().count(getEntityClass(), cnd));
        Pagination pagination = this.listPage(pager.getPageNumber(),pager.getPageSize(),sql);
        List<T> list = (List<T>) pagination.getList();//TODO 强转待测试
        if (!Strings.isBlank(linkname)) {
            this.dao().fetchLinks(list, linkname);
        }
        dataTablesResult.setData(list);
        dataTablesResult.setDraw(draw);
        dataTablesResult.setRecordsTotal(length);
        return dataTablesResult;
    }

    public T fetchById(long id){
        return this.dao().fetch(this.getEntityClass(), id);
    }

    public T fetchById(String name){
        return this.dao().fetch(this.getEntityClass(), name);
    }

    public T fetchOne(Sql sql){
        sql.setEntity(getEntity());
        sql.setCallback(new ListEntityCallback(null));
        dao().execute(sql);
        List<T> list = sql.getList(getEntityClass());
        if(null != list && list.size() >= 1){
            return list.get(0);
        }
        return null;
    }

    public Record fetchOne2Record(Sql sql){
        sql.setCallback(new FetchRecordCallback());
        dao().execute(sql);
        List<Record> list = sql.getList(Record.class);
        if(null != list && list.size() >= 1){
            return list.get(0);
        }
        return null;
    }

    public T fetchOne(Condition cnd){
        List<T> list = this.dao().query(getEntityClass(), cnd);
        if(null != list && list.size() >= 1){
            return list.get(0);
        }
        return null;
    }

    public Record fetchOne2Record(Condition cnd){
        List<Record> list = this.dao().query(getEntity().getTableName(),cnd);
        if(null != list && list.size() >= 1){
            return list.get(0);
        }
        return null;
    }

    /**
     * 通过LONG主键获取部分字段值
     *
     * @param id
     * @param fieldName 支持通配符 ^(a|b)$
     * @return
     */
    public T getField(long id,String fieldName) {
        return Daos.ext(this.dao(), FieldFilter.create(getEntityClass(), fieldName))
                .fetch(getEntityClass(), id);
    }

    /**
     * 通过INT主键获取部分字段值
     *
     * @param id
     * @param fieldName 支持通配符 ^(a|b)$
     * @return
     */
    public T getField(int id,String fieldName) {
        return Daos.ext(this.dao(), FieldFilter.create(getEntityClass(), fieldName))
                .fetch(getEntityClass(), id);
    }


    /**
     * 通过NAME主键获取部分字段值
     *
     * @param name
     * @param fieldName 支持通配符 ^(a|b)$
     * @return
     */
    public T getField(String name,String fieldName) {
        return Daos.ext(this.dao(), FieldFilter.create(getEntityClass(), fieldName))
                .fetch(getEntityClass(), name);
    }

    public int getMaxId() {
        return this.dao().getMaxId(this.getEntityClass());
    }

    public Sql execute(Sql sql){
        return this.dao().execute(sql);
    }

//    public void execute(Sql... sql){
//        this.dao().execute(sql);
//    }

    public Long count(Sql sql){
        sql.setCallback(new FetchLongCallback());
        dao().execute(sql);
        return sql.getLong();
    }

}
